<?php
declare (strict_types=1);

namespace app\index\service;

use app\index\model\Commodity;
use app\index\model\Coupon;
use app\index\model\MallBase;
use app\index\model\Order;
use app\index\model\User;
use app\index\model\UserCoupon as admin;
use app\utils\Addons;
use app\utils\SendMsg;
use think\Exception;
use think\model\Relation;

class UserCouponService extends BaseService
{

    public function findAll($status = null, int $page = 1, int $size = 10)
    {
        if (is_null($status)) {
            $list = admin::where(['user_id' => $this->user['id']])
                ->field("id,merchant_id,coupon_id,status,create_time,update_time")
                ->with(['merchant', 'coupon' => function (Relation $query) {
                    $query->hidden(
                        [
                            'total_limit_type',
                            'total_limit',
                            'sort',
                            'get_type',
                            'used',
                            'status',
                            'create_time',
                            'update_time',
                            'delete_time'
                        ]);
                }])
                ->hidden(['merchant_id'])
                ->order('id', 'DESC')
                ->paginate(['page' => $page, 'list_rows' => $size]);
        } else {
            $list = admin::where(['user_id' => $this->user['id']])
                ->where('status', $status)
                ->field("id,merchant_id,coupon_id,status,create_time,update_time")
                ->with(['merchant', 'coupon' => function (Relation $query) {
                    $query->hidden(
                        [
                            'total_limit_type',
                            'total_limit',
                            'sort',
                            'get_type',
                            'used',
                            'create_time',
                            'update_time',
                            'delete_time'
                        ]);
                }])
                ->hidden(['merchant_id'])
                ->order('id', 'DESC')
                ->paginate(['page' => $page, 'list_rows' => $size]);
        }

        return [HTTP_SUCCESS, $list];
    }

    public function findByCommodity(array $data)
    {
        $moneyAll = 0;
        $ids = $ret = $commodityAll = $merchantId = $merchantIdUsedType = $commodityData = [];
        foreach ($data AS $key => $value)
            $ids = array_merge(array_column($value, 'commodity'), $ids);
        $list = Commodity::where('id', 'in', $ids)
            ->field('id,classify_id,sell_price,has_sku,merchant_id')
            ->with(['sku.skuInventory'])
            ->order('create_time DESC')
            ->select()
            ->toArray();
        if (empty($list))
            throw new Exception("商品不存在", HTTP_NOTACCEPT);
        foreach ($list AS $key => $value)
            $commodityData[$value['merchant_id']][$value['id']] = $value;
        foreach ($data as $key => $value) {
            //商品数组
            $merchantId[] = $key;
            $merchantIdUsedType[] = $key . '1';
            $moneyTotal = 0;
            $commodityList = [];
            foreach ($value as $index => $item) {
                $attach = empty($item['attach']) ? [] : $item['attach'];
                $price = \think\facade\Db::name('attach_value')
                    ->where('id', 'IN', $attach)
                    ->sum('price');
                if (empty($commodityData[$key][$item['commodity']]))
                    throw new Exception("商品不存在", HTTP_NOTACCEPT);
                $array = $commodityData[$key][$item['commodity']];
                $inventory = new InventoryService($array['id']);
                if (empty($array['has_sku']))
                    $money = $inventory->getPrice();
                else
                    $money = $inventory->getPrice((int)$item['sku']);
                $money = $price + $money;


                $commodityList[$array['id']] = [
                    "commodity_id" => $array['id'],
                    "classify_id" => $array['classify_id'],
                    "discount" => $money,
                    "count" => $item['count'],
                ];
                $money = bcmul((string)$money, (string)$item['count']);
                $moneyTotal = bcadd((string)$moneyTotal, (string)$money);

            }
            $commodityAll = array_merge($commodityAll, $commodityList);
            $moneyAll = bcadd((string)$moneyAll, (string)$moneyTotal);


            $userCouponList = admin::where(['status' => 0, 'user_id' => $this->user['id'], 'merchant_id' => $key, 'used_type' => 1])
                ->field("id,coupon_id,create_time,update_time")
                ->with(['coupon' => function (Relation $query) {
                    $query->hidden(
                        [
                            'total_limit_type',
                            'total_limit',
                            'sort',
                            'get_type',
                            'used',
                            'status',
                            'create_time',
                            'update_time',
                            'delete_time'
                        ]);
                }])
                ->select();

            list($available, $cashOut, $timeOut) = $this->matchCoupon($userCouponList, $commodityList, $moneyTotal);
            $ret[$key] = ['available' => $available, 'cashOut' => $cashOut, 'timeOut' => $timeOut];

        }

        $userCouponAll = admin::where(['status' => 0, 'user_id' => $this->user['id'], 'merchant_id' => 1, 'used_type' => 0])
            ->field("id,coupon_id,create_time,update_time")
            ->with(['coupon' => function (Relation $query) {
                $query->hidden(
                    [
                        'total_limit_type',
                        'total_limit',
                        'sort',
                        'get_type',
                        'used',
                        'status',
                        'create_time',
                        'update_time',
                        'delete_time'
                    ]);
            }])
            ->select();

        list($availableAll, $cashOutAll, $timeOutAll) = self::matchCoupon($userCouponAll, $commodityAll, $moneyAll);
        $ret['base'] = ['available' => $availableAll, 'cashOutAll' => $cashOutAll, 'timeOutAll' => $timeOutAll];

        return [HTTP_SUCCESS, $ret];
    }

    public function findByOrder($order)
    {
        $order = Order::where(['status' => 1, 'user_id' => 2, 'order_no' => $order])
            ->field('id,money,merchant_id')
            ->with(['orderCommodityHidden.commodityHidden'])
            ->find()->toArray();

        $commodityList = self::getOrderCommodity($order);

        $moneyTotal = $order['money'];

        $userCouponList = admin::where(['status' => 0, 'user_id' => $this->user['id'], 'merchant_id' => $order['merchant_id']])
            ->field("id,coupon_id,create_time,update_time")
            ->with(['coupon' => function (Relation $query) {
                $query->hidden(
                    [
                        'total_limit_type',
                        'total_limit',
                        'sort',
                        'get_type',
                        'used',
                        'status',
                        'create_time',
                        'update_time',
                        'delete_time'
                    ]);
            }])
            ->select();

        list($available, $cashOut, $timeOut) = self::matchCoupon($userCouponList, $commodityList, $moneyTotal);
        return [HTTP_SUCCESS, ['available' => $available, 'cashOut' => $cashOut, 'timeOut' => $timeOut]];
    }


    public function findAllByOrder(array $orderList)
    {

        $orders = Order::where(['status' => 1, 'user_id' => 2, 'order_no' => $orderList])
            ->field('id,money')
            ->with(['orderCommodityHidden.commodityHidden'])
            ->select()->toArray();

        $commodityList = self::getCommodity($orders);

        $moneyTotal = array_sum(array_column($orders, 'money'));

        $userCouponList = admin::where(
            [
                'status' => 0,
                'user_id' => $this->user['id'],
                'used_type' => 1,
                'merchant_id' => 1
            ]
        )
            ->field("id,coupon_id,create_time,update_time")
            ->with(['coupon' => function (Relation $query) {
                $query->hidden([
                    'total_limit_type',
                    'total_limit',
                    'sort',
                    'get_type',
                    'used',
                    'status',
                    'create_time',
                    'update_time',
                    'delete_time'
                ]);
            }])
            ->select();
        list($available, $cashOut, $timeOut) = self::matchCoupon($userCouponList, $commodityList, $moneyTotal);
        return [HTTP_SUCCESS, ['available' => $available, 'cashOut' => $cashOut, 'timeOut' => $timeOut]];
    }


    private function matchCoupon($userCouponList, $commodityList, $moneyTotal)
    {
        $cashOut = [];
        $timeOut = [];
        $available = [];

        foreach ($userCouponList as $k => $v) {
            if (empty($v->coupon)) continue;
            if ($v->coupon->conditions > $moneyTotal) {
                $cashOut[] = $v;
                continue;
            }

            if ($v->coupon->time_limit_type == 0 &&
                time() > strtotime($v->create_time . "+" . $v->coupon->limit_time . "day")) {
                $timeOut[] = $v;
                continue;
            } elseif ($v->coupon->time_limit_type == 1 &&
                (time() > strtotime($v->coupon->limit_indate_end) || time() < strtotime($v->coupon->limit_indate_begin))) {
                $timeOut[] = $v;
                continue;
            }

            if ($v->coupon->commodity_limit_type == 1) {
                $allowList = explode(",", $v->coupon->commodity_limit);
                $diffCommodity = array_intersect_key($commodityList, array_flip(array_diff(array_keys($commodityList), $allowList)));
                $diffMoney = 0;
                foreach ($diffCommodity as $i => $j) {
                    bcadd((string)$diffMoney, bcmul((string)$j['count'], (string)$j['discount']));
                }
                $money = bcsub((string)$moneyTotal, (string)$diffMoney);
                if ($money < $v->coupon->conditions) {
                    $cashOut[] = $v;
                    continue;
                }
            } elseif ($v->coupon->commodity_limit_type == 2) {
                $allowList = explode(",", $v->coupon->commodity_limit);

                $diffCommodity = array_intersect_key($commodityList, array_flip(array_intersect(array_keys($commodityList), $allowList)));
                $diffMoney = 0;
                foreach ($diffCommodity as $i => $j) {
                    bcadd((string)$diffMoney, bcmul((string)$j['count'], (string)$j['discount']));
                }
                $money = bcsub((string)$moneyTotal, (string)$diffMoney);
                if ($money < $v->coupon->conditions) {
                    $cashOut[] = $v;
                    continue;
                }
            } elseif ($v->coupon->commodity_limit_type == 3) {
                $allowList = explode(",", $v->coupon->commodity_limit);
                $allowList = array_map(function ($var) {
                    return explode("->", $var);
                }, $allowList);
                $diffCommodity = [];
                foreach ($commodityList as $i => $j) {
                    $explode = explode("->", $j['classify_id']);
                    $flag = self::arrayMatch($explode, $allowList);
                    if (!$flag)
                        $diffCommodity[] = $j;
                }
                $diffMoney = 0;
                foreach ($diffCommodity as $i => $j) {
                    bcadd((string)$diffMoney, bcmul((string)$j['count'], (string)$j['discount']));
                }
                $money = bcsub((string)$moneyTotal, (string)$diffMoney);
                if ($money < $v->coupon->conditions) {
                    $cashOut[] = $v;
                    continue;
                }
            }
            $available[] = $v;
        }

        return [$available, $cashOut, $timeOut];
    }

    private function arrayMatch($explode, $allowList)
    {
        $flag = false;
        foreach ($allowList as $index => $item) {
            $diff = array_diff($item, $explode);
            if (empty($diff)) {
                $flag = true;
                break;
            }
        }
        return $flag;
    }


    private function getCommodity(array $orders)
    {
        $commodityList = [];
        foreach ($orders as $k => $v) {
            foreach ($v['orderCommodityHidden'] as $i => $j) {
                $commodityList[$j['commodity_id']] = $j;
            }
        }
        return $commodityList;
    }


    private function getOrderCommodity(array $order)
    {
        $commodityList = [];
        foreach ($order['orderCommodityHidden'] as $i => $j) {
            $commodityList[$j['commodity_id']] = $j;
        }
        return $commodityList;
    }


    public function save(int $couponId)
    {
        $coupon = Coupon::where(['id' => $couponId])->lock(true)->find();
        if (!$coupon)
            throw new Exception('优惠券不存在', HTTP_NOTACCEPT);

        if ($coupon->total_limit_type == 1 && $coupon->total_limit <= $coupon->used) {
            $coupon->status = 2;
            $coupon->save();
            throw new Exception('优惠券不足', HTTP_NOTACCEPT);
        }

        if ($coupon->user_limit_type == 1) {
            $userCouponList = admin::where(['user_id' => $this->user['id'], 'coupon_id' => $couponId])
                ->select()->toArray();
            if ($coupon->user_limit < sizeof($userCouponList) + 1)
                return [HTTP_NOTACCEPT, '超过限领'];
        }

        $model = admin::create(
            [
                'user_id' => $this->user['id'],
                'coupon_id' => $couponId,
                'merchant_id' => $coupon->merchant_id,
                'used_type' => $coupon->used_type,
            ]
        );

        $coupon->used += 1;
        $coupon->save();
//
//        $coupon->inc('used')->update();

        global $mid;
        $check = Addons::check($mid, 4);

        if ($check) {
            $msgData = array(
                'mall_name' => MallBase::getBase(),
                'coupon_user' => $this->user['nickname'],
                'coupon_name' => $coupon->name,
                'coupon_validity' => $coupon->time_limit_type == 0 ?
                    "有效期至: " . date("Y-m-d H:i:s", strtotime("+" . $coupon->limit_time . " day")) :
                    "有效期至: " . $coupon->limit_indate_end,
                'coupon_time' => date("Y-m-d H:i:s")
            );

            $sendMsg = new SendMsg("优惠券发放通知");
            $user = User::find($this->user['id']);

            try {
                $url = !empty($user->UserWx->type) && $user->UserWx->type == 1 ?
                    request()->domain() . "/pages/conpon/conpon?type=0&mall_id=".$mid :
                    request()->domain() . "/h5/#/pages/conpon/conpon?type=0&mall_id=".$mid;

                $sendMsg->send($user, $msgData, $url);
            } catch (Exception $e) {

            }
        }

        return [HTTP_CREATED, $model];

    }


    public function read(int $id)
    {
        $model = admin::where(['status' => 0, 'user_id' => $this->user['id']])
            ->field("id,coupon_id,create_time,update_time")
            ->with(['coupon' => function (Relation $query) {
                $query->hidden(
                    [
                        'total_limit_type',
                        'total_limit',
                        'sort',
                        'get_type',
                        'used',
                        'status',
                        'create_time',
                        'update_time',
                        'delete_time'
                    ]);
            }])
            ->find($id);
        if (!$model)
            throw new Exception("优惠券不可用", HTTP_NOTACCEPT);
        if ($model->coupon->time_limit_type == 0 &&
            time() > strtotime($model->create_time . "+" . $model->coupon->limit_time . "day"))
            throw new Exception("优惠券不可用", HTTP_NOTACCEPT);
        elseif ($model->coupon->time_limit_type == 1 &&
            (time() > strtotime($model->coupon->limit_indate_end) || time() < strtotime($model->coupon->limit_indate_begin)))
            throw new Exception("优惠券不可用", HTTP_NOTACCEPT);
        return [HTTP_SUCCESS, $model];
    }

}
